package common.util;

import java.util.Calendar;
import java.util.Iterator;
import java.util.Locale;

import model.entity.Item;
import model.entity.Product;
import model.entity.container.ProductContainer;
import model.entity.container.ProductGroup;
import model.entity.container.StorageUnit;
import model.management.StorageUnitManager;

public class Tools
{
  public static void printStackTrace()
  {
    System.out.printf("Logging stack trace of thread '%s'\n", Thread.currentThread().getName());
    StackTraceElement[] trace = Thread.currentThread().getStackTrace();
    for (int i = 2; i < trace.length; i++)
    {
      StackTraceElement e = trace[i];
      System.out.printf("\tat %s.%s(%s:%d)\n", e.getClassName(), e.getMethodName(),
          e.getFileName(), e.getLineNumber());
    }
  }

  public static void printStackTrace(int length)
  {
    System.out.printf("Logging stack trace of thread '%s'\n", Thread.currentThread().getName());
    StackTraceElement[] trace = Thread.currentThread().getStackTrace();
    for (int i = 2; i < 2 + length; i++)
    {
      StackTraceElement e = trace[i];
      System.out.printf("\tat %s.%s(%s:%d)\n", e.getClassName(), e.getMethodName(),
          e.getFileName(), e.getLineNumber());
    }
  }

  public static void printStackTraceAtLogLevel(int logLevel)
  {
    System.out.printf("Logging stack trace of thread '%s'\n", Thread.currentThread().getName());
    StackTraceElement[] trace = Thread.currentThread().getStackTrace();
    for (int i = 2; i < trace.length; i++)
    {
      StackTraceElement e = trace[i];
      System.out.printf("\tat %s.%s(%s:%d)\n", e.getClassName(), e.getMethodName(),
          e.getFileName(), e.getLineNumber());
    }
  }

  public static void printStackTraceAtLogLevel(int logLevel, int length)
  {
    System.out.printf("Logging stack trace of thread '%s'\n", Thread.currentThread().getName());
    StackTraceElement[] trace = Thread.currentThread().getStackTrace();
    for (int i = 2; i < 2 + length; i++)
    {
      StackTraceElement e = trace[i];
      System.out.printf("\tat %s.%s(%s:%d)\n", e.getClassName(), e.getMethodName(),
          e.getFileName(), e.getLineNumber());
    }
  }

  public static String getLink()
  {
    StackTraceElement element = Thread.currentThread().getStackTrace()[2];
    return String.format("%s.%s(%s:%s)", element.getClassName(), element.getMethodName(),
        element.getFileName(), element.getLineNumber());
  }

  public static String getCallerLink()
  {
    StackTraceElement element = Thread.currentThread().getStackTrace()[3];
    return String.format("%s.%s(%s:%s)", element.getClassName(), element.getMethodName(),
        element.getFileName(), element.getLineNumber());
  }

  public static boolean traceContainsMethodName(String name)
  {
    for (StackTraceElement element : Thread.currentThread().getStackTrace())
    {
      if (element.getMethodName().contains(name))
        return true;
    }
    return false;
  }

  public static boolean traceContainsClassName(String name)
  {
    for (StackTraceElement element : Thread.currentThread().getStackTrace())
    {
      if (element.getClassName().contains(name))
        return true;
    }
    return false;
  }

  public static StackTraceElement getFirstStackTraceElementWithClassName(String name)
  {
    for (StackTraceElement element : Thread.currentThread().getStackTrace())
    {
      if (element.getClassName().contains(name))
        return element;
    }
    return null;
  }

  public static StackTraceElement getFirstStackTraceElementWithClassName(String name,
      StackTraceElement[] trace)
  {
    for (StackTraceElement element : trace)
    {
      if (element.getClassName().contains(name))
        return element;
    }
    return null;
  }

  public static String getTimeStamp()
  {
    Calendar time = Calendar.getInstance();
    int hour = time.get(Calendar.HOUR_OF_DAY);
    int min = time.get(Calendar.MINUTE);
    int sec = time.get(Calendar.SECOND);
    int ms = time.get(Calendar.MILLISECOND);

    String timeStamp = String.format("%02d:%02d:%02d.%03d", hour, min, sec, ms);

    return timeStamp;
  }

  public static String getLongTimeStamp()
  {
    Calendar time = Calendar.getInstance();
    int day = time.get(Calendar.DAY_OF_MONTH);
    String month = time.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.US);
    int year = time.get(Calendar.YEAR);
    int hour = time.get(Calendar.HOUR_OF_DAY);
    int min = time.get(Calendar.MINUTE);
    int sec = time.get(Calendar.SECOND);
    int ms = time.get(Calendar.MILLISECOND);

    String timeStamp = String.format("%02d %3s %04d %02d:%02d:%02d.%03d", day, month, year, hour,
        min, sec, ms);

    return timeStamp;
  }

  public static boolean echo(boolean b)
  {
    return b;
  }

  private static int indent = 0;

  private static String getIndent()
  {
    if (indent > 0)
      return String.format("%" + indent + "s", " ");
    else
      return "";
  }

  public static void resetIndent()
  {
    indent = 0;
  }

  private static void indent()
  {
    indent += 2;
  }

  private static void unindent()
  {
    indent -= 2;
    if (indent < 0)
      indent = 0;
  }

  public static void logMethodEntered()
  {
    System.out.println(getIndent() + "Starting method " + getCallerLink());
    indent();
  }

  public static void logMethodEntered(String paramNames, Object... params)
  {
    String[] names = paramNames.split(",");
    System.out.printf("%sStarting method %s with parameters (%d):\n%1$s{\n", getIndent(),
        getCallerLink(), params.length);
    for (int i = 0; i < names.length; i++)
      System.out.printf("%s  %s: %s\n", getIndent(), names[i], params[i]);
    System.out.println(getIndent() + "}");
    indent();
  }

  public static void logMethodExited()
  {
    unindent();
    System.out.println(getIndent() + "Ending method " + getCallerLink());
  }

  public static void logMethodExited(Object result)
  {
    unindent();
    System.out.printf("%sEnding method %s with result %s\n", getIndent(), getCallerLink(), result);
  }

  private static int indentStorage = 0;

  public static void printContainerTree()
  {
    System.out.println("  >>  Printing container tree  >>");
    indentStorage = indent;
    indent = 4;

    Iterator<StorageUnit> units = StorageUnitManager.getStorageUnits();
    while (units.hasNext())
    {
      StorageUnit unit = units.next();
      System.out.println(getIndent() + unit);
      indent();
      printProductContainer(unit);
      unindent();
    }

    indent = indentStorage;
    System.out.println("  <<  Finished printing container tree  <<");
  }

  private static void printProductContainer(ProductContainer container)
  {
    Iterator<Product> products = container.getProducts();
    while (products.hasNext())
    {
      Product product = products.next();
      System.out.println(getIndent() + printProduct(product));
      indent();
      Iterator<Item> items = container.getItems(product);
      while (items.hasNext())
      {
        Item item = items.next();
        System.out.println(getIndent() + printItem(item));
      }
      unindent();
    }

    Iterator<ProductGroup> groups = container.getProductGroups();
    while (groups.hasNext())
    {
      ProductGroup group = groups.next();
      System.out.println(getIndent() + group);
      indent();
      printProductContainer(group);
      unindent();
    }
  }

  private static String printProduct(Product product)
  {
    return String.format("Product [description=%s,barcode=%s]", product.getDescription(), product
        .getBarcode().getCode());
  }

  private static String printItem(Item item)
  {
    return String.format("Item [barcode=%s]", item.getBarcode().getCode());
  }
}